#include "config_loader.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
using namespace std;

#define INVALID_LINE -1

namespace utility
{
    //erase ' ''\t''\r''\n'
    void trim_string(string& Str)
    {
        if(Str.empty() || Str == "")
            return;
        Str.erase(Str.find_last_not_of(" \t\r\n")+1);
        Str.erase(0,Str.find_first_not_of(" \t\r\n"));
    }

    void ConfigLoader::construct()
    {
        _file_buf.clear();
        _add_file.clear();
        _seg_list.clear();
        _filename = "";
    }

    bool ConfigLoader::load_from(const char* filename)
    {
        _filename = filename;
        ifstream ifile;
        ifile.open(filename,ios::in);
        if(!ifile)
        {
            return false;
        }

        string stringbuf;
        int count = 0;
        while(getline(ifile,stringbuf))
        {
            _file_buf.push_back(stringbuf);
            size_t firstchar = stringbuf.find_first_not_of(" \t\r\n");
            if(firstchar != string::npos)
            {
                if(firstchar == stringbuf.find('['))
                {
                    ConfigSegNode seg(count);
                    _seg_list.push_back(seg);
                }
                else if(firstchar!=string::npos && firstchar != stringbuf.find('#')
                        && firstchar != stringbuf.find(';'))
                {
                    if(_seg_list.size()==0)
                    {
                        ConfigSegNode seg(count);
                        _seg_list.push_back(seg);
                    }
                    _seg_list[_seg_list.size()-1].add_item(count);
                }
            }
            count++;
        }
        return true;
    }

    bool ConfigLoader::save()
    {
        if (_filename == "")
            return "";
        vector<string>::iterator iter;
        for(size_t i = 0; i < _seg_list.size(); i++)
        {
            if(_seg_list[i]._attribute == ATTRIB_ADD)
            {
                _seg_list[i]._attribute = ATTRIB_NORMAL;
                _file_buf.push_back(_add_file[_seg_list[i]._line_index]);
                _seg_list[i]._line_index = _file_buf.size() - 1;
                _seg_list[i]._last_line = _file_buf.size();
            }
            for(size_t j = 0; j < _seg_list[i]._item_list.size(); j++)
            {
                if (_seg_list[i]._item_list[j]._attribute == ATTRIB_ADD)
                {
                    iter = _file_buf.begin() + _seg_list[i]._last_line;
                    _file_buf.insert(iter, _add_file[_seg_list[i]._item_list[j]._line_index]);
                    _seg_list[i]._item_list[j]._attribute = ATTRIB_NORMAL;
                    _seg_list[i]._item_list[j]._line_index = _seg_list[i]._last_line;
                    _seg_list[i]._last_line++;
                    for (size_t x = i + 1; x < _seg_list.size() - 1; x++)
                    {
                        if (_seg_list[x]._attribute == ATTRIB_ADD)
                            continue;
                        _seg_list[x]._line_index++;
                        _seg_list[x]._last_line++;
                        for (size_t y = 0; y < _seg_list[x]._item_list.size(); y++)
                        {
                            if (_seg_list[x]._item_list[y]._attribute == ATTRIB_ADD)
                                continue;
                            _seg_list[x]._item_list[y]._line_index++;
                        }
                    }
                }
            }
        }
        _add_file.clear();
        ofstream ofile;
        ofile.open(_filename.c_str());

        for(size_t i=0;i<_file_buf.size();i++)
        {
            ofile << _file_buf[i] << endl;
        }
        ofile.close();
        return true;
    }

    string ConfigLoader::get_segment(const string& str)
    {
        string nstr = "";
        size_t firstchar = str.find_first_not_of(" \t\r\n");
        if(firstchar == str.find('['))
        {
            nstr = str.substr(str.find_first_of("[") + 1,
                    str.find_first_of("]") - str.find_first_of("[") - 1);
        }
        else
        {
            nstr = "";
        }
        trim_string(nstr);
        return nstr;
    }

    string ConfigLoader::get_target(const string& str)
    {
        string nstr = "";
        nstr=str.substr(0,str.find('='));
        trim_string(nstr);
        return nstr;
    }

    string ConfigLoader::get_value(const string& str)
    {
        string nstr = "";
        nstr = str.substr(str.find('=') + 1);
        trim_string(nstr);
        nstr = nstr.substr(0, nstr.find_first_of(" \t\r\n#;"));
        trim_string(nstr);
        return nstr;
    }

    ConfigLoader::ConfigSegNode* ConfigLoader::find_segment
                (const string& segment)
    {
        for(size_t i = 0; i < _seg_list.size(); i++)
        {
            string temp = _seg_list[i]._attribute == ATTRIB_ADD ?
                                    _add_file[_seg_list[i]._line_index] :
                                    _file_buf[_seg_list[i]._line_index];
            if(segment == get_segment(temp))
                return &_seg_list[i];
        }
        return NULL;
    }

    ConfigLoader::ConfigItemNode* ConfigLoader::find_target
                (const string& target, const string& segment)
    {
        string temp;
        if(segment!="")
        {
            for(size_t i = 0;i< _seg_list.size(); i++)
            {
                temp=_seg_list[i]._attribute == ATTRIB_ADD ?
                                _add_file[_seg_list[i]._line_index] :
                                _file_buf[_seg_list[i]._line_index];
                if(segment == get_segment(temp))
                {
                    for(size_t j = 0; j < _seg_list[i]._item_list.size(); j++)
                    {
                        temp = _seg_list[i]._item_list[j]._attribute == ATTRIB_ADD ?
                                            _add_file[_seg_list[i]._item_list[j]._line_index] :
                                            _file_buf[_seg_list[i]._item_list[j]._line_index];
                        if(target == get_target(temp))
                            return &_seg_list[i]._item_list[j];
                    }
                }
            }
        }
        else
        {
            for(size_t i = 0; i < _seg_list.size(); i++)
            {
                for(size_t j = 0; j < _seg_list[i]._item_list.size(); j++)
                {
                    temp = _seg_list[i]._item_list[j]._attribute == ATTRIB_ADD ?
                                        _add_file[_seg_list[i]._item_list[j]._line_index] :
                                        _file_buf[_seg_list[i]._item_list[j]._line_index];
                    if(target == get_target(temp))
                        return &_seg_list[i]._item_list[j];
                }
            }
        }
        return NULL;
    }

    void ConfigLoader::add_segment(const string& segment)
    {
        ConfigSegNode seg(_add_file.size());
        seg._attribute = ATTRIB_ADD;
        seg._last_line = _file_buf.size();
        string addseg = "[ " + segment + " ]";
        _add_file.push_back(addseg);
        _seg_list.push_back(seg);
    }



    string ConfigLoader::get_value_of
                (const string& target, const string& segment)
    {
        if (_filename == "")
            return "";
        ConfigItemNode* pItem = (find_target(target,segment));
        if(pItem != NULL)
        {
            return get_value(_file_buf[pItem->_line_index]);
        }
        else
            return "";
    }
    string ConfigLoader::set_value_of
                (const string& target, const string& nvalue, const string& segment)
    {
        if (_filename == "")
            return "";
        ConfigItemNode* pItem = find_target(target,segment);
        if(pItem!=NULL)
        {
            int line = pItem->_line_index;
            string source = _file_buf[line];
            string ret = source.substr(0, source.find('=') + 1);
            source = source.substr(source.find('=') + 1);
            ret += source.substr(0, source.find_first_not_of(" \t\r\n"));
            source.erase(0, source.find_first_not_of(" \t\r\n"));
            source.erase(0, source.find_first_of(" \t\r\n#;"));
            ret += nvalue;
            ret += source;
            _file_buf[line] = ret;
            pItem->_attribute = ATTRIB_MODIFY;
            return ret;
        }
        else
        {
            ConfigSegNode* pSeg = NULL;

            pSeg = find_segment(segment);
            if(pSeg == NULL)
            {
                add_segment(segment);
                pSeg = &_seg_list[_seg_list.size() - 1];
            }

            ConfigItemNode item(_add_file.size());
            item._attribute = ATTRIB_ADD;
            string additem = target + "\t=\t" + nvalue;
            _add_file.push_back(additem);
            pSeg->_item_list.push_back(item);
            return additem;
        }
    }
}
